//
//  TcpClientWrapperTolua.cpp
//  rnet
//
//  Created by will on 22/08/2017.
//
//

#include "TcpClientWrapperTolua.hpp"
#include "client-cpp/TcpClientWrapper.hpp"
#include "tolua_fix.h"
#include "CCLuaEngine.h"

#include <chrono>

using namespace rnet;
using namespace cocos2d;

static bool s_ioScheduled = false;

class LuaTcpClientWrapper
{
public:
    LuaTcpClientWrapper();
    virtual ~LuaTcpClientWrapper();
    
    TcpClientWrapper& getClient(){return _client;}
    
private:
    TcpClientWrapper _client;
    std::chrono::seconds  _lastPostTime;
};

LuaTcpClientWrapper::LuaTcpClientWrapper()
{
    if(!s_ioScheduled)
    {
        Director::getInstance()->getScheduler()->schedule([](float){
            TcpClientWrapper::updateIOService();
        }, this, 0, CC_REPEAT_FOREVER, 0, false, "updateIOService");
        s_ioScheduled = true;
    }
}

LuaTcpClientWrapper::~LuaTcpClientWrapper()
{
    ScriptHandlerMgr::getInstance()->removeObjectAllHandlers(this);
}

static int tolua_collect_TcpClientWrappe (lua_State* tolua_S)
{
    LuaTcpClientWrapper* self = (LuaTcpClientWrapper*) tolua_tousertype(tolua_S,1,0);
    Mtolua_delete(self);
    return 0;
}

static int tolua_TcpClientWrapper_create(lua_State* tolua_S)
{
    LuaTcpClientWrapper *socket = new (std::nothrow) LuaTcpClientWrapper();
    tolua_pushusertype(tolua_S,(void*)socket,"cc.TcpSocket");
    tolua_register_gc(tolua_S,lua_gettop(tolua_S));
    return 1;
}

static int tolua_TcpClientWrapper_connect(lua_State* tolua_S)
{
    LuaTcpClientWrapper* self    = (LuaTcpClientWrapper*)  tolua_tousertype(tolua_S,1,0);
    
    int argc = lua_gettop(tolua_S) - 1;

    std::string ip =  tolua_tostring(tolua_S,2,0);
    unsigned short port = tolua_tonumber(tolua_S, 3, 0);
    int timeout = 10;
    
    int handler = -1 ;
    if(argc>=3)
    {
        handler = toluafix_ref_function(tolua_S,4,-1);
    }
    if(argc>=4 && lua_isnumber(tolua_S, 5))
    {
        timeout = tolua_tonumber(tolua_S, 5, timeout);
    }

    TcpClientWrapper::ConnectingCallback cb = [self,handler](int code,const std::string& msg, const std::string& ip){
        if(handler>0)
        {
            LuaStack* stack = LuaEngine::getInstance()->getLuaStack();
            stack->pushInt(code);
            stack->pushString(msg.c_str());
            stack->pushString(ip.c_str());
            stack->executeFunctionByHandler(handler, 3);
            LuaEngine::getInstance()->removeScriptHandler(handler);
        }
    };
    
    self->getClient().connect(ip, port,cb,timeout);
    
    return 1;
}

static int tolua_TcpClientWrapper_disconnect(lua_State* tolua_S)
{
    LuaTcpClientWrapper* self    = (LuaTcpClientWrapper*)  tolua_tousertype(tolua_S,1,0);
    
    self->getClient().disconnect();
    return 0;
}

static int tolua_TcpClientWrapper_isConnected(lua_State* tolua_S)
{
    LuaTcpClientWrapper* self    = (LuaTcpClientWrapper*)  tolua_tousertype(tolua_S,1,0);
    tolua_pushboolean(tolua_S, self->getClient().isConnected());
    return 1;
}

static int tolua_TcpClientWrapper_send(lua_State* tolua_S)
{
    LuaTcpClientWrapper* self    = (LuaTcpClientWrapper*)  tolua_tousertype(tolua_S,1,0);
    
    size_t size = 0;
    const char* data = (const char*) lua_tolstring(tolua_S, 2, &size);
    if ( NULL == data)
    {
        lua_pushboolean(tolua_S, false);
        return 1;
    }
    
    if(!lua_isnumber(tolua_S, 3))
    {
        lua_pushboolean(tolua_S, false);
        return 1;
    }
    uint32_t command = lua_tonumber(tolua_S, 3);
    
    self->getClient().send(data,size,command);

    lua_pushboolean(tolua_S, true);
    return 1;
}

static int tolua_TcpClientWrapper_setKeepAliveTime(lua_State* tolua_S)
{
    LuaTcpClientWrapper* self    = (LuaTcpClientWrapper*)  tolua_tousertype(tolua_S,1,0);
    
    int seconds = lua_tonumber(tolua_S, 2);
    int interval = lua_tonumber(tolua_S, 3);
    
    self->getClient().setKeepAliveTime(seconds, interval);
    
    return 0;
}

static int tolua_TcpClientWrapper_setEventsCallback(lua_State* tolua_S)
{
    LuaTcpClientWrapper* self    = (LuaTcpClientWrapper*)  tolua_tousertype(tolua_S,1,0);
    
    int handler = -1;
    if(!lua_isnil(tolua_S, 2))
    {
        handler = (  toluafix_ref_function(tolua_S,2,-1));
    }
    
    
    ScriptHandlerMgr::getInstance()->removeObjectAllHandlers((void*)self);
    
    if(handler>0)
    {
        ScriptHandlerMgr::getInstance()->addCustomHandler((void*)self, handler);
        auto& client = self->getClient();
        client.ConnectedCallback = nullptr;
        client.ConnectedCallback = [handler](TcpClientWrapper* sender){
            LuaStack* stack = LuaEngine::getInstance()->getLuaStack();
            stack->pushString("connected");
            stack->executeFunctionByHandler(handler, 1);
        };
        
        client.MessageCallback = nullptr;
		client.MessageCallback = [handler](TcpClientWrapper* sender, uint32_t msgId, const char* data, size_t size){
            LuaStack* stack = LuaEngine::getInstance()->getLuaStack();
            stack->pushString("message");
			stack->pushInt(msgId);
            stack->pushString(data, size);
            stack->executeFunctionByHandler(handler, 3);
        };

        client.DisconnectedCallback = nullptr;
        client.DisconnectedCallback = [handler](TcpClientWrapper* sender, const char* error_msg){
            LuaStack* stack = LuaEngine::getInstance()->getLuaStack();
            stack->pushString("disconnected");
            stack->pushString(error_msg);
            stack->executeFunctionByHandler(handler, 2);
        };
    }
    else
    {
        self->getClient().ConnectedCallback = nullptr;
        self->getClient().MessageCallback = nullptr;
        self->getClient().DisconnectedCallback = nullptr;
    }
    
    return 0;
}

TOLUA_API int register_tcpclientwrapper_module(lua_State* L)
{
    lua_getglobal(L, "_G");
    if (lua_istable(L,-1))//stack:...,_G,
    {
        tolua_open(L);
        tolua_usertype(L, "cc.TcpSocket");
        tolua_module(L,"cc",0);
        tolua_beginmodule(L,"cc");
        tolua_cclass(L,"TcpSocket","cc.TcpSocket","",tolua_collect_TcpClientWrappe);
        tolua_beginmodule(L,"TcpSocket");
        tolua_function(L, "create", tolua_TcpClientWrapper_create);
        tolua_function(L, "connect", tolua_TcpClientWrapper_connect);
        tolua_function(L, "disconnect", tolua_TcpClientWrapper_disconnect);
        tolua_function(L, "isConnected", tolua_TcpClientWrapper_isConnected);
        tolua_function(L, "send", tolua_TcpClientWrapper_send);
        tolua_function(L, "setEventsCallback", tolua_TcpClientWrapper_setEventsCallback);
        tolua_function(L, "setKeepAliveTime", tolua_TcpClientWrapper_setKeepAliveTime);
        tolua_endmodule(L);
        tolua_endmodule(L);
    }
    lua_pop(L, 1);
    return 1;
}
